/*
 * PSplitPacket.cpp
 *
 *  Created on: May 28, 2014
 *      Author: yuliang
 */

#include "PSplitPacket.h"

#include <assert.h>
#ifdef WIN32
	#include <winsock2.h>
#else
	#include <arpa/inet.h>
#endif

#define SUCCESS "成功."
#define ERR_HASCOMPLATE_PKG "存在整包未取出."
#define ERR_NONECOMPLATE_PKG "不存在整包."

enum ErrorType {
	None = 0,
	HasComplate,
	NoneComplate,
};

static const char* ErrorArray[] = {
		SUCCESS, ERR_HASCOMPLATE_PKG, ERR_NONECOMPLATE_PKG
};

typedef struct ChessPacketHandler {
	uint32_t m_originalLen; // 单个数据最大长度
	uint32_t m_totallen; // m_data长度
	char * m_data; // 存放原始数据的buf,大小是原始数                                                                                                   据的三倍
	uint32_t m_startpos; // 起始包的位置
	uint32_t m_startlen; // 起始包的长度
	uint32_t m_nextpos; // 下一个包的位置
	uint32_t m_copylen; // 下一个包的位置
	uint32_t m_errno; // 存放当前操作的错误码
} _CPkgH;

CPDHANDLE chpktCreate(uint32_t max_size) {
	CPDHANDLE _h = (CPDHANDLE)malloc(sizeof(_CPkgH));
	if (!_h)
		goto ERR;

	_h->m_originalLen = max_size;
	// 3倍大小
	_h->m_totallen = (max_size << 1) + max_size;
	_h->m_data = (char*)malloc(_h->m_totallen);
	memset(_h->m_data, '\0', _h->m_totallen);
	if (!_h->m_data) {
		free(_h);
		goto ERR;
	}

	_h->m_startpos = 0;
	_h->m_startlen = 0;
	_h->m_nextpos = 0;
	_h->m_copylen = 0;
	_h->m_errno = None;

	return _h;
ERR:
	return NULL;
}

void chpktRelease(CPDHANDLE h) {
	if (h) {
		if (h->m_data)
			free(h->m_data);
		free(h);
	}
}

const char* chpkgGetLastErrorMessage(CPDHANDLE h) {
	if (h->m_errno >= sizeof(ErrorArray))
		return 0;
	return ErrorArray[h->m_errno];
}

int chpkgPushData(CPDHANDLE handle, const char* d, uint32_t s) {
	//此函数中应检查是否有完整的包未处理（调用chpkgHasComplatePacket())，如果
	//有，说明本库的使用者没有按正确的步骤使用本库，则需返回错误，并将错误信息
	//保存到 ChessPacketHandler的err_msg中
	if (!handle || !s)
		return -1;

	assert(s <= handle->m_originalLen);

	if (chpkgHasComplatePacket(handle))
		handle->m_errno = HasComplate;
	else {
		// start1: 处理PKGSIZEBYTE个char
		// 存有数据的起点
		char* pkgdata = handle->m_data + handle->m_startpos;
		{
			// 还需要保存PKGSIZEBYTE个char的大小
			register uint32_t _needheadlen = 0;
			// 已保存数据是否大于PKGSIZEBYTE个char
			if (handle->m_copylen < PKGSIZEBYTE) {
				_needheadlen = PKGSIZEBYTE - handle->m_copylen;
				_needheadlen = _needheadlen<s?_needheadlen:s;

				memcpy(pkgdata + handle->m_copylen, d, _needheadlen);
				handle->m_copylen += _needheadlen;
				d += _needheadlen;
				s -= _needheadlen;
			}
		}
		// end1;

		if (s) {
			// start2: 处理当前包
			{
				// 当前包的长度
				uint32_t _pkglen = ntohl(*((uint32_t*)pkgdata));
				// 当前包还缺少的长度
				register uint32_t _needcopy = _pkglen - handle->m_copylen + PKGSIZEBYTE;
				// 此次需要copy的长度
				_needcopy = _needcopy<s?_needcopy:s;
				memcpy(pkgdata + handle->m_copylen, d, _needcopy);
				handle->m_copylen += _needcopy;
				d += _needcopy;
				s -= _needcopy;

				// 如何完成整包,记录当前整包的长度
				if (handle->m_copylen == _pkglen + PKGSIZEBYTE) {
					handle->m_startlen = _pkglen;
					//如果不加大括号，在VC下就编译不过，真操蛋！！
					{
					// 有效数据前的内存长度
					uint32_t _frontsize = handle->m_startpos;
					// 有效数据后的内存长度
					uint32_t _behindsize = handle->m_totallen - handle->m_startpos - handle->m_copylen;
					// 记录下个包的位置
					if (_frontsize <= _behindsize)
						handle->m_nextpos = handle->m_startpos + handle->m_copylen;
					else
						handle->m_nextpos = 0;
					}
				}
			}
			// end2;

			if (s) {
				// start3: 处理下个包
				// 从下个包的位置保存数据
				memcpy(handle->m_data + handle->m_nextpos, d, s);
				handle->m_copylen += s;
				s = 0;
				// end;
			}
		}
		handle->m_errno = None;
	}

	return handle->m_errno;
}

void chpkgCurrentPacketDone(CPDHANDLE h) {
	//在此函数中应把当前包扔掉，然后将后面的数据移到缓冲的最开始
	//除此只外，其它所有函数都不会修改或扔掉包数据
	if (!h)
		return;
	if (chpkgHasComplatePacket(h)) {
		h->m_copylen -= (h->m_startlen + PKGSIZEBYTE);
		h->m_startpos = h->m_nextpos;
		// 查看是否还有整包
		if (h->m_copylen > PKGSIZEBYTE) {
			char* pkgdata = h->m_data + h->m_startpos;
			register uint32_t _pkglen = ntohl(*((uint32_t*)pkgdata));
			if (_pkglen + PKGSIZEBYTE < h->m_copylen) {
				h->m_startlen = _pkglen;
				h->m_nextpos = h->m_startpos + _pkglen + PKGSIZEBYTE;
			} else if (_pkglen + PKGSIZEBYTE == h->m_copylen) {
				h->m_startlen = _pkglen;
				{
					// 有效数据前的内存长度
					uint32_t _frontsize = h->m_startpos;
					// 有效数据后的内存长度
					uint32_t _behindsize = h->m_totallen - h->m_startpos - h->m_copylen;
					// 记录下个包的位置
					if (_frontsize <= _behindsize)
						h->m_nextpos = h->m_startpos + h->m_copylen;
					else
						h->m_nextpos = 0;
				}
			} else
				h->m_startlen = 0;
		} else
			h->m_startlen = 0;
	} else
		h->m_errno = NoneComplate;
}

int chpkgHasComplatePacket(CPDHANDLE h) {
	if (!h)
		return 0;
	if (h->m_startlen == 0 && h->m_copylen >= PKGSIZEBYTE && 0 == ntohl(*((uint32_t*)(h->m_data + h->m_startpos))))
		return 1;
	if (h->m_startlen == 0)
		return 0;
	return 1;
}

uint32_t chpkgGetCurrentPacketLength(CPDHANDLE h) {
	if (!h)
		return 0;

	//注意，不算最前面表示长度的byes
	if (chpkgHasComplatePacket(h))
		return (uint32_t)h->m_startlen;
	h->m_errno = NoneComplate;
	return 0;
}

char* chpkgGetCurrentPacketPtr(CPDHANDLE h) {
	if (!h)
		return 0;

	if (chpkgHasComplatePacket(h))
		return h->m_data + (h->m_startpos + PKGSIZEBYTE);
	h->m_errno = NoneComplate;
	return 0;
}

void chpkgCopyCurrentPacketAndLength(char* buf_copy_to, CPDHANDLE h) {
	//注意，算上4字节的长度了
	if (!h)
		return;
	if (chpkgHasComplatePacket(h))
		memcpy(buf_copy_to, h->m_data + h->m_startpos, h->m_startlen);
	else
		h->m_errno = NoneComplate;
}
